package com.siwei.apply.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

/**
 * 文件解压工具类
 */
public class FileExtractUtil {
    
    private static final Logger logger = LoggerFactory.getLogger(FileExtractUtil.class);
    
    /**
     * 判断是否为压缩文件
     *
     * @param filePath 文件路径
     * @return 是否为压缩文件
     */
    public static boolean isCompressedFile(String filePath) {
        if (filePath == null || filePath.trim().isEmpty()) {
            return false;
        }
        
        String lowerCasePath = filePath.toLowerCase();
        return lowerCasePath.endsWith(".zip") || 
               lowerCasePath.endsWith(".rar") ||
               lowerCasePath.endsWith(".7z");
    }
    
    /**
     * 解压文件到指定目录
     *
     * @param compressedFilePath 压缩文件路径
     * @param extractDir 解压目录路径
     * @return 是否解压成功
     */
    public static boolean extractFile(String compressedFilePath, String extractDir) {
        try {
            if (compressedFilePath == null || extractDir == null) {
                logger.error("解压文件失败：文件路径或解压目录不能为空");
                return false;
            }
            
            Path compressedFile = Paths.get(compressedFilePath);
            if (!Files.exists(compressedFile)) {
                logger.error("解压文件失败：压缩文件不存在: {}", compressedFilePath);
                return false;
            }
            
            // 创建解压目录
            Path extractPath = Paths.get(extractDir);
            Files.createDirectories(extractPath);
            
            String lowerCasePath = compressedFilePath.toLowerCase();
            if (lowerCasePath.endsWith(".zip")) {
                return extractZipFile(compressedFilePath, extractDir);
            } else if (lowerCasePath.endsWith(".rar")) {
                logger.warn("暂不支持RAR格式解压: {}", compressedFilePath);
                return false;
            } else if (lowerCasePath.endsWith(".7z")) {
                logger.warn("暂不支持7Z格式解压: {}", compressedFilePath);
                return false;
            } else {
                logger.error("不支持的压缩格式: {}", compressedFilePath);
                return false;
            }
            
        } catch (Exception e) {
            logger.error("解压文件异常，压缩文件: {}, 解压目录: {}", compressedFilePath, extractDir, e);
            return false;
        }
    }
    
    /**
     * 解压文件到同名文件夹
     *
     * @param compressedFilePath 压缩文件路径
     * @return 解压目录路径，失败返回null
     */
    public static String extractToSameNameFolder(String compressedFilePath) {
        try {
            if (compressedFilePath == null || compressedFilePath.trim().isEmpty()) {
                logger.error("解压文件失败：压缩文件路径不能为空");
                return null;
            }
            
            Path compressedFile = Paths.get(compressedFilePath);
            if (!Files.exists(compressedFile)) {
                logger.error("解压文件失败：压缩文件不存在: {}", compressedFilePath);
                return null;
            }
            
            // 生成同名文件夹路径
            String fileName = compressedFile.getFileName().toString();
            String baseName = fileName.substring(0, fileName.lastIndexOf('.'));
            Path extractDir = compressedFile.getParent().resolve(baseName);
            
            boolean success = extractFile(compressedFilePath, extractDir.toString());
            if (success) {
                logger.info("成功解压文件到同名文件夹，压缩文件: {}, 解压目录: {}", compressedFilePath, extractDir);
                return extractDir.toString();
            } else {
                logger.error("解压文件到同名文件夹失败，压缩文件: {}", compressedFilePath);
                return null;
            }
            
        } catch (Exception e) {
            logger.error("解压文件到同名文件夹异常，压缩文件: {}", compressedFilePath, e);
            return null;
        }
    }
    
    /**
     * 解压ZIP文件
     *
     * @param zipFilePath ZIP文件路径
     * @param destDir 目标目录
     * @return 是否解压成功
     */
    private static boolean extractZipFile(String zipFilePath, String destDir) {
        // 尝试多种编码方式解压ZIP文件
        Charset[] charsets = {
            Charset.forName("GBK"),     // 中文Windows系统常用编码
            Charset.forName("GB2312"),  // 简体中文编码
            Charset.forName("UTF-8"),   // 标准UTF-8编码
            Charset.defaultCharset()    // 系统默认编码
        };

        for (Charset charset : charsets) {
            try {
                if (extractZipFileWithCharset(zipFilePath, destDir, charset)) {
                    logger.info("成功解压ZIP文件: {} 到目录: {}，使用编码: {}", zipFilePath, destDir, charset.name());
                    return true;
                }
            } catch (Exception e) {
                logger.debug("使用编码 {} 解压失败，尝试下一种编码: {}", charset.name(), e.getMessage());
            }
        }

        logger.error("所有编码方式都无法解压ZIP文件: {}", zipFilePath);
        return false;
    }

    /**
     * 使用指定编码解压ZIP文件
     *
     * @param zipFilePath ZIP文件路径
     * @param destDir 目标目录
     * @param charset 字符编码
     * @return 是否解压成功
     */
    private static boolean extractZipFileWithCharset(String zipFilePath, String destDir, Charset charset) throws IOException {
        try (ZipFile zipFile = new ZipFile(zipFilePath, charset)) {
            java.util.Enumeration<? extends ZipEntry> entries = zipFile.entries();

            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                Path filePath = Paths.get(destDir, entry.getName());

                // 安全检查：防止目录遍历攻击
                if (!filePath.normalize().startsWith(Paths.get(destDir).normalize())) {
                    logger.warn("检测到潜在的目录遍历攻击，跳过文件: {}", entry.getName());
                    continue;
                }

                if (entry.isDirectory()) {
                    Files.createDirectories(filePath);
                } else {
                    // 确保父目录存在
                    Files.createDirectories(filePath.getParent());

                    // 写入文件
                    try (InputStream is = zipFile.getInputStream(entry);
                         OutputStream os = Files.newOutputStream(filePath)) {
                        byte[] buffer = new byte[8192];
                        int length;
                        while ((length = is.read(buffer)) > 0) {
                            os.write(buffer, 0, length);
                        }
                    }
                }
            }

            return true;
        }
    }
    
    /**
     * 获取目录结构
     *
     * @param directory 目录路径
     * @return 目录结构Map
     */
    public static Map<String, Object> getDirectoryStructure(Path directory) {
        try {
            if (!Files.exists(directory)) {
                logger.warn("目录不存在: {}", directory);
                return null;
            }
            
            Map<String, Object> structure = new HashMap<>();
            structure.put("name", directory.getFileName().toString());
            structure.put("type", Files.isDirectory(directory) ? "directory" : "file");
            structure.put("path", directory.toString());
            
            if (Files.isDirectory(directory)) {
                List<Map<String, Object>> children = new ArrayList<>();
                
                try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory)) {
                    for (Path entry : stream) {
                        if (Files.isDirectory(entry)) {
                            children.add(getDirectoryStructure(entry));
                        } else {
                            children.add(getFileStructure(entry));
                        }
                    }
                }
                
                structure.put("children", children);
            } else {
                // 如果是文件，添加文件大小
                try {
                    structure.put("size", Files.size(directory));
                } catch (IOException e) {
                    structure.put("size", 0);
                }
            }
            
            return structure;
            
        } catch (IOException e) {
            logger.error("获取目录结构异常，目录: {}", directory, e);
            return null;
        }
    }
    
    /**
     * 获取文件结构
     *
     * @param file 文件路径
     * @return 文件结构Map
     */
    public static Map<String, Object> getFileStructure(Path file) {
        Map<String, Object> structure = new HashMap<>();
        structure.put("name", file.getFileName().toString());
        structure.put("type", "file");
        structure.put("path", file.toString());
        
        try {
            structure.put("size", Files.size(file));
        } catch (IOException e) {
            logger.warn("获取文件大小失败: {}", file, e);
            structure.put("size", 0);
        }
        
        return structure;
    }
}
